package com.huawei.experiencereuse;

import android.content.Context;
import android.net.ConnectivityManager;
import android.nfc.Tag;
import android.util.Log;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import okhttp3.*;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.nio.charset.StandardCharsets;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/*
 * 用于加载和匹配经验
 */
public class ExperienceManager {
    private static final String TAG = "ExperienceManager";
    private static final String experienceFileName = "experience.json";
    private static ExperienceManager instance;
    private JSONArray experiences; // 经验保存在experience.json文件中，而且是一个json数组
    private static final OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)  // 连接超时（对应 HttpClient 的 connectTimeout）
            .readTimeout(60, TimeUnit.SECONDS)     // 读取超时（对应 HttpRequest 的 timeout）
            .writeTimeout(30, TimeUnit.SECONDS)    // 写入超时
            .callTimeout(120, TimeUnit.SECONDS)
            .build();
    private static final String API_KEY = "sk-tpnewxvjcqsrvwxxzdkjhvaxlasyxcrxxmoucnwxgwqxdebe";
    private static final String API_URL = "https://api.siliconflow.cn/v1/chat/completions";
    public static final String MODEL_NAME = "Qwen/Qwen2.5-7B-Instruct";//"Qwen/Qwen2.5-7B-Instruct";//"Pro/Qwen/Qwen2.5-7B-Instruct";
    private final ObjectMapper objectMapper;
    private String fineTuneString = "";
    private ExperienceManager(Context context) {

        StringBuilder jsonContent = new StringBuilder();
        this.objectMapper = new ObjectMapper();
        try {
            // 读取json文件的内容
            InputStream is = context.getAssets().open(experienceFileName);
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            String line;
            while ((line = reader.readLine()) != null) {
                jsonContent.append(line);
            }
            reader.close();
            is.close();

            // 解析json数组
            this.experiences = new JSONArray(jsonContent.toString());
            Log.i(TAG, "Load experience successfully");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (JSONException e) {
            Log.e(TAG, "failed to parse experience json file");
            e.printStackTrace();
        }
    }

    // 异步发送请求的方法

    private static final ExecutorService executor = Executors.newSingleThreadExecutor();
    public static ExperienceManager getInstance(Context context) {
        if (instance == null) {
            instance = new ExperienceManager(context);
        }
        return instance;
    }

    public JSONObject getMatchedExperience(String intent) {
        try {
            if (intent.indexOf("打车") != -1) {
                Log.i(TAG, "匹配到打车经验");
                return experiences.getJSONObject(0);
            } else if (intent.indexOf("酒店") != -1) {
                Log.i(TAG, "匹配到预定酒店经验");
                return experiences.getJSONObject(1);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return new JSONObject();
    }

    // TODO: 对经验进行微调
    public String fineTuningExperience(String intent, JSONObject experience) throws ExecutionException, InterruptedException {
        if (experience.length() == 0) {
            return "";
        }
        CountDownLatch latch = new CountDownLatch(1);

        Thread finetuneThread = new Thread(() -> finetune(intent, experience, new FinetuneCallback() {
            @Override
            public void onSuccess(String result) {
                Log.i(TAG, "微调成功");
                fineTuneString = result;
                Log.i(TAG, "微调结果： " + fineTuneString);
                latch.countDown(); // 截图线程结束，则计数器减1
            }

            @Override
            public void onFailure(String errorMessage) {
                Log.i(TAG, errorMessage);
                latch.countDown(); // 截图线程结束，则计数器减1
                // 考虑如何处理截图失败的情况，例如重试或通知用户
            }
        }));
        finetuneThread.start();
        boolean isCompleted = latch.await(120, java.util.concurrent.TimeUnit.SECONDS);
        if (isCompleted) {
            Log.i(TAG, "微调经验完成");
            // 此处处理子线程的执行结果
        } else {
            Log.i(TAG, "主线程：等待微调线程超时（超过120秒）");
        }
//        fineTuneString = "{\"id\":\"0198cc9ece805118319a4ba219b9d20d\",\"object\":\"chat.completion\",\"created\":1755779616,\"model\":\"Pro/Qwen/Qwen2.5-7B-Instruct\",\"choices\":[{\"index\":0,\"message\":{\"role\":\"assistant\",\"content\":\"```json\\n{\\n    \\\"id\\\":0,\\n    \\\"name\\\":\\\"高德打车\\\",\\n    \\\"intent\\\":\\\"使用高德APP打车到环球金融中心\\\",\\n    \\\"steps\\\":[\\n        {\\n            \\\"id\\\":0,\\n            \\\"desc\\\":\\\"打开高德地图应用\\\",\\n            \\\"actions\\\":[\\n                {\\\"type\\\":\\\"click\\\",\\\"value\\\":[866,610]},\\n                {\\\"type\\\":\\\"sleep\\\",\\\"value\\\":5}\\n            ],\\n            \\\"pre_check\\\":null,\\n            \\\"post_check\\\":\\\"已经成功打开了高德地图应用，在高德地图的底部导航栏中，我可以看到“打车”选项。\\\"\\n        },\\n        {\\n            \\\"id\\\":1,\\n            \\\"desc\\\":\\\"点击底部导航栏中的\\\\\\\"打车\\\\\\\"图标。\\\",\\n            \\\"actions\\\":[\\n                {\\\"type\\\":\\\"click\\\",\\\"value\\\":[697,966]},\\n                {\\\"type\\\":\\\"sleep\\\",\\\"value\\\":5}\\n            ],\\n            \\\"pre_check\\\":\\\"已经成功打开了高德地图应用，在高德地图的底部导航栏中，我可以看到“打车”选项。\\\",\\n            \\\"post_check\\\":\\\"已经成功进入了高德打车服务界面。\\\"\\n        },\\n        {\\n            \\\"id\\\":2,\\n            \\\"desc\\\":\\\"点击\\\\\\\"你要去哪儿\\\\\\\"输入框\\\",\\n            \\\"actions\\\":[\\n                {\\\"type\\\":\\\"click\\\",\\\"value\\\":[263,646]},\\n                {\\\"type\\\":\\\"sleep\\\",\\\"value\\\":5}\\n            ],\\n            \\\"pre_check\\\":\\\"已经成功进入了高德打车服务界面。\\\",\\n            \\\"post_check\\\":\\\"已经成功进入了高德打车服务界面，并且输入框已经激活。\\\"\\n        },\\n        {\\n            \\\"id\\\":3,\\n            \\\"desc\\\":\\\"输入\\\\\\\"环球金融中心\\\\\\\"作为目的地\\\",\\n            \\\"actions\\\":[\\n                {\\\"type\\\":\\\"type\\\",\\\"value\\\":\\\"环球金融中心\\\"},\\n                {\\\"type\\\":\\\"sleep\\\",\\\"value\\\":5}\\n            ],\\n            \\\"pre_check\\\":\\\"已经成功进入了高德打车服务界面，并且输入框已经激活。\\\",\\n            \\\"post_check\\\":\\\"已经在高德地图的打车界面输入了\\\\\\\"环球金融中心\\\\\\\"作为目的地，并且系统显示了相关的地点选项。\\\"\\n        },\\n        {\\n            \\\"id\\\":4,\\n            \\\"desc\\\":\\\"点击第一个选项，选择环球金融中心为目的地\\\",\\n            \\\"actions\\\":[\\n                {\\\"type\\\":\\\"click\\\",\\\"value\\\":[511,303]},\\n                {\\\"type\\\":\\\"sleep\\\",\\\"value\\\":5}\\n            ],\\n            \\\"pre_check\\\":\\\"已经在高德地图的打车界面输入了\\\\\\\"环球金融中心\\\\\\\"作为目的地，并且系统显示了相关的地点选项。\\\",\\n            \\\"post_check\\\":\\\"已经成功选择了\\\\\\\"环球金融中心\\\\\\\"作为目的地，并且系统显示了多个打车选项。\\\"\\n        },\\n        {\\n            \\\"id\\\":6,\\n            \\\"desc\\\":\\\"点击\\\\\\\"立即打车\\\\\\\"按钮\\\",\\n            \\\"actions\\\":[\\n                {\\\"type\\\":\\\"click\\\",\\\"value\\\":[434,249]},\\n                {\\\"type\\\":\\\"sleep\\\",\\\"value\\\":5}\\n            ],\\n            \\\"pre_check\\\":\\\"已经成功选择了\\\\\\\"环球金融中心\\\\\\\"作为目的地，并且系统显示了多个打车选项。\\\",\\n            \\\"post_check\\\":\\\"已经成功选择了\\\\\\\"环球金融中心\\\\\\\"作为目的地，并且系统正在努力为你叫车。\\\"\\n        },\\n        {\\n            \\\"id\\\":7,\\n            \\\"desc\\\":\\\"等待叫车完成，结束\\\",\\n            \\\"actions\\\":[\\n                {\\\"type\\\":\\\"finished\\\",\\\"value\\\":\\\"已成功用高德打车到环球金融中心\\\"}\\n            ],\\n            \\\"pre_check\\\":\\\"由于系统正在自动处理叫车请求，应该等待叫车完成。此时不需要进行任何操作，等待系统完成叫车流程即可\\\",\\n            \\\"post_check\\\":null\\n        }\\n    ]\\n}\\n```\"},\"finish_reason\":\"stop\"}],\"usage\":{\"prompt_tokens\":674,\"completion_tokens\":744,\"total_tokens\":1418},\"system_fingerprint\":\"\"}\n";
        try {
            // 1. 解析最外层响应
            ObjectNode root = (ObjectNode) objectMapper.readTree(fineTuneString);
            Log.i(TAG, "step 1 down ");
            // 2. 取 assistant 的 content
            String markdown = root.path("choices")
                    .get(0).path("message").path("content").asText();
            Log.i(TAG, "step 2 down " + markdown);

            markdown = markdown.trim();
            markdown = extractBracketContent(markdown);


            // 2. 用正则把中文逗号或英文逗号作为分隔符
            String[] arr = markdown.split("[，,]");
            // 3. 去掉每个元素前后的引号和空白
            for (int i = 0; i < arr.length; i++) {
                arr[i] = arr[i].trim().replaceAll("^\"|\"$", "");
            }
            // 打印验证
            System.out.println("打印验证: " + Arrays.toString(arr));  // 输出 ["金融中心", "金融大厦"]

            String template = experience.toString();

            String result = replacePlaceholders(template, arr);
            System.out.println("模板匹配结果： " + result);

            return result;
        } catch (Exception e) {
            System.err.println("[ERROR] Failed to parse response: " + e.getMessage());
            return "Error";
        }
    }

    private void finetune(String intent, JSONObject experience, FinetuneCallback callback) {
        Log.i(TAG,"进入finetune函数");
        try {
            // 构建消息
            String content = "现在有一个经验模板和一段用户的请求，经验模板中有若干个需要填充的参数，用 $n 来表示，比如第一个需要填充的参数为 $1 ，第二个需要填充的参数为 $2 ，以此类推。现在请你将用户的请求进行信息提取，拆解成需要的若干个参数，使经验模板可以变成一段可以执行的具体指令。" +
                    "用户请求：" + intent + "。" +
                    "经验模板：" + experience.toString() + "。" +
                    "请以list的形式输出这些参数，只需要返回这些参数即可。";

            // 使用JSONObject构建请求体
            JSONObject message = new JSONObject();
            message.put("role", "user");
            message.put("content", content);

            JSONArray messages = new JSONArray();
            messages.put(message);

            JSONObject requestBody = new JSONObject();
            requestBody.put("model", MODEL_NAME);
            requestBody.put("messages", messages);
            requestBody.put("max_tokens", 1024);
            requestBody.put("temperature", 0.7);

            String json = requestBody.toString();

            RequestBody body = RequestBody.create(json, MediaType.get("application/json; charset=utf-8"));

            Request request = new Request.Builder()
                    .url(API_URL)
                    .addHeader("Authorization", "Bearer " + API_KEY)
                    .post(body)
                    .build();

            new Thread(()->{
                try {
                    try (Response response = client.newCall(request).execute()) {
                        if (response.isSuccessful()) {
                            callback.onSuccess(response.body().string());
                        } else {
                            Log.e(TAG, "Request failed: " + response.code());
                            callback.onFailure("微调失败");
                        }
                    }
                } catch (Exception e) {
                    // 切换到主线程回调错误信息
                    String errorMsg = "请求失败：" + e.getMessage();
                    Log.e(TAG, errorMsg, e);
                    callback.onFailure(errorMsg);
                }
            }).start();
        } catch (Exception e) {
            System.err.println("[ERROR] Analysis failed: " + e.getMessage());
        }
    }

    public static String replacePlaceholders(String template, String[] arr) {
        if (template == null || arr == null) {
            return template;
        }

        // 正则：匹配 $1、$2 … $n（n 为 1~99999 的正整数）
        Pattern p = Pattern.compile("\\$(\\d+)");
        Matcher m = p.matcher(template);

        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            int index = Integer.parseInt(m.group(1)) - 1; // 把 $1 映射到 arr[0]
            String replacement;
            if (index < arr.length) {
                replacement = arr[index];
            } else {
                replacement = arr[arr.length - 1];
                System.err.printf("[WARN] Index out of range: $%d, using last element \"%s\"%n",
                        index + 1, replacement);
            }
            // 防止 replacement 本身包含 $ 或 \ 导致异常
            m.appendReplacement(sb, Matcher.quoteReplacement(replacement));
        }
        m.appendTail(sb);
        return sb.toString();
    }

    private static String extractBracketContent(String str) {
        if (str == null) return str;

        int left = str.indexOf('[');
        if (left == -1) return str;          // 没有 '['

        int depth = 1;
        int right = -1;
        for (int i = left + 1; i < str.length(); i++) {
            char c = str.charAt(i);
            if (c == '[') depth++;
            else if (c == ']') {
                depth--;
                if (depth == 0) {           // 找到匹配的 ']'
                    right = i;
                    break;
                }
            }
        }

        if (right == -1) return str;        // 没找到配对的 ']'
        return str.substring(left + 1, right);
    }
}
